home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue31 / webcomm / WEBTRANS.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1998-01-14  |  22.6 KB  |  670 lines

  1. unit webtrans;
  2.  
  3. // TCustomTransactionQueuer and derivatives
  4. // for managing transaction processing in
  5. // Web commerce applications
  6. //
  7. // (c) 1997 South Pacific Information Services Ltd
  8. //  http://www.spis.co.nz      software@spis.co.nz
  9. //
  10. // Permission is granted for the adaptation and use
  11. // of this code provided this message is maintained
  12. // and this source code is not onsold without
  13. // explicit permission from SPIS Ltd.
  14.  
  15. interface
  16.  
  17. uses
  18.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
  19.  
  20.  
  21. type
  22.  TTransactionStatus = ( tsInvalid, tsQueue, tsCancel, tsProcessing,
  23.                         tsTimeOut, tsAccept, tsReject);
  24.  
  25.  TTransactionData =
  26.   record
  27.     TransactionID: string; { a unique identifier, e.g. surfer name, session ID }
  28.     TransactionType: string;{ May be used, e.g. if doing authorisations (not confirmed sales)
  29.                               with settlement happening LATER after shipping is confirmed }
  30.     MerchantID: string;    { only needed if the back end is multi-merchant enabled }
  31.     MerchantPassword: string; { as above, assuming the back end requires a password }
  32.     Comment: string;       { available with some services -- appears on form }
  33.     Clerk: string;         { ditto }
  34.     CardNumber: string;    { no spaces or punctuation }
  35.     ExpiryMonth: string;   { 01..12 }
  36.     ExpiryYear: string;    { 98, 99, 2000, 2001... }
  37.     TransactionAmount: string; { total amount to be authorised or transferred }
  38.     Age: TDatetime;        { Time when transaction was first queued, set by Queue, not caller }
  39.     TransactionStatus: TTransactionStatus; { set by Queuer or returned from Processor }
  40.   end;
  41.   // Notes about TTransactionData:
  42.   // 1) these components handle queuing/processing, NOT
  43.   //    validation! Therefore, they assume the information provided
  44.   //    via the above structure has been checked beforehand, and contains no
  45.   //    punctuation, spaces or other spurious information which may be rejected by
  46.   //    the processing service
  47.   // 2) Not all the above fields may be used by each implementation -- the
  48.   //    assumption is that the important fields will be used and preserved
  49.   //    by whatever file/memory queueing schema is adopted in each case
  50.  
  51. type
  52.   // Abstract class which implements the most common logic:
  53.   TCustomTransactionQueuer = class(TComponent)
  54.   private
  55.     fTransactionData: TTransactionData;
  56.     fQueueByFile: Boolean;       // File/Memory require implementation in derived classes
  57.     fEncryptionPassword: string; // if encryption is implemented
  58.   protected
  59.     function LoadTransaction: Boolean;
  60.  
  61.     // derived classes must implement these methods to support File, Memory or both:
  62.     // The functions should return False if the Transaction is not present
  63.     // The 'Save' procedures should recognise a tsCancel status and
  64.     // handle it accordingly -- in most cases just by deleting the entry...
  65.     procedure SaveTransactionToFile; virtual; abstract;
  66.     function LoadTransactionFromFile: Boolean; virtual; abstract;
  67.     procedure DeleteTransactionFromFile; virtual; abstract;
  68.     procedure SaveTransactionToMemory; virtual; abstract;
  69.     function LoadTransactionFromMemory: Boolean; virtual; abstract;
  70.     procedure DeleteTransactionFromMemory; virtual; abstract;
  71.  
  72.   public
  73.     procedure QueueTransaction;
  74.     procedure CheckTransaction;
  75.     procedure CancelTransaction;
  76.     procedure DeleteTransaction;
  77.     procedure SaveTransaction; // only publically used for Processor components
  78.     function GetStatusMessage: String; virtual;
  79.   published
  80.     property TransactionData: TTransactionData read fTransactionData write fTransactionData;
  81.     property StatusMessage:string read GetStatusMessage;
  82.   end;
  83.  
  84.  
  85.   // Base class for file-based queuing
  86.   TCustomFileTransactionQueuer = class(TCustomTransactionQueuer)
  87.   private
  88.      fQueueDirectory: string;
  89.      procedure SetQueueDirectory(const value: string);
  90.   protected
  91.     constructor Create(AOwner: TComponent); override;
  92.  
  93.  
  94.     // here's an implemented set of core file-based methods:
  95.     function LoadTransactionFromFile: Boolean; override;
  96.     procedure SaveTransactionToFile; override;
  97.     procedure DeleteTransactionFromFile; override;
  98.  
  99.     // these ones MAY need overriding in derived classes
  100.     function GetTransactionFileName: string; virtual;
  101.     function GetLoadFileName: string; virtual;
  102.     function GetSaveFileName: string; virtual;
  103.     function GetProcessedFileName: string; virtual;
  104.  
  105.     // these two MUST be implemented in derived classes:
  106.     procedure ReadDataFromBuffer(const buffer: string); virtual; abstract;
  107.     procedure WriteDataToBuffer(var buffer: string); virtual; abstract;
  108.  
  109.     // this one should be implemented ONLY if separate "process result" files
  110.     // are returned (e.g. by ICVerify)
  111.     function LoadProcessedResult(const filename: string): Boolean; virtual; abstract;
  112.  
  113.   published
  114.     property QueueDirectory: string read fQueueDirectory write SetQueueDirectory;
  115.   end;
  116.  
  117.   // example derived class for exceptionally simple file-based queuing
  118.   TSimpleFileTransactionQueuer = class(TCustomFileTransactionQueuer)
  119.   private
  120.   protected
  121.      // custom routines overridden for this implementation:
  122.     function GetTransactionFileName: string; override;
  123.     procedure ReadDataFromBuffer(const buffer: string);override;
  124.     procedure WriteDataToBuffer(var buffer: string); override;
  125.   public
  126.   published
  127.   end;
  128.  
  129.   // example derived class for processing TSimpleFileTransactionQueuer queues
  130.   TSimpleFileTransactionProcessor = class(TSimpleFileTransactionQueuer)
  131.   private
  132.   protected
  133.   public
  134.      function GetNextTransaction: Boolean;
  135.   published
  136.   end;
  137.  
  138.  
  139.   // derived class for ICVerify's file-based queuing
  140.   TICVerifyTransactionQueuer = class(TCustomFileTransactionQueuer)
  141.   private
  142.     fTransactionList: TStringList;
  143.   protected
  144.      // custom routines overridden for this class:
  145.     procedure DeleteTransactionFromFile; override;
  146.     function GetLoadFileName: string; override;
  147.     function GetSaveFileName: string; override;
  148.     function GetProcessedFileName: string; override;
  149.     procedure ReadDataFromBuffer(const buffer: string);override;
  150.     procedure WriteDataToBuffer(var buffer: string); override;
  151.     function LoadProcessedResult(const filename: string): Boolean; override;
  152.  
  153.     // new routine:
  154.     function GetFileNameForThisTransaction: string;
  155.  
  156.   public
  157.     constructor Create(aOwner: TComponent); override;
  158.     destructor Destroy; override;
  159.   published
  160.   end;
  161.  
  162.  
  163. procedure Register;
  164.  
  165. implementation
  166.  
  167. // ### TCustomTransactionQueuer component
  168. //
  169. // Abstract class at the heart of all that follows
  170.  
  171. const TransactionStatusMessages : array[TTransactionStatus] of string =
  172.   ( 'Invalid',
  173.     'Queued and awaiting processing',
  174.     'Canceled',
  175.     'Now processing',
  176.     'Timed out',
  177.     'Accepted',
  178.     'Rejected');
  179.  
  180. procedure TCustomTransactionQueuer.QueueTransaction;
  181. begin
  182.    if not LoadTransaction then // isn't already there?
  183.    with TransactionData do
  184.    begin
  185.       TransactionStatus := tsQueue;
  186.       Age := now;
  187.       SaveTransaction;
  188.    end;
  189. end;
  190.  
  191. // Note: before calling CheckTransaction, you MUST set all relevant
  192. // fields, just as you would for a Queue. This is because if the Check
  193. // doesn't find the transaction in the queue already, it re-establishes
  194. // it, thus providing for a fail-safe recovery if, for example,
  195. // the queuing has been cleared by a system failure or restart.
  196.  
  197. procedure TCustomTransactionQueuer.CheckTransaction;
  198. begin
  199.   QueueTransaction;  // check for existence, re-establish if not!
  200. end;
  201.  
  202. procedure TCustomTransactionQueuer.CancelTransaction;
  203. begin
  204.    if LoadTransaction then // there?
  205.    begin
  206.      with TransactionData do
  207.      begin
  208.         if TransactionStatus = tsQueue then // still Queued ok
  209.         begin
  210.           TransactionStatus := tsCancel;
  211.           DeleteTransaction; // if it fails, it Loads again in case status has changed
  212.         end; { otherwise, caller must respond to actual status returned by original Load }
  213.      end
  214.    end else
  215.      with TransactionData do TransactionStatus := tsInvalid;
  216. end;
  217.  
  218. procedure TCustomTransactionQueuer.SaveTransaction;
  219. begin
  220.    if fQueueByFile then
  221.       SaveTransactionToFile
  222.    else
  223.       SaveTransactionToMemory;
  224. end;
  225.  
  226. function TCustomTransactionQueuer.LoadTransaction: Boolean;
  227. begin
  228.    if fQueueByFile then
  229.       result := LoadTransactionFromFile
  230.    else
  231.       result := LoadTransactionFromMemory;
  232. end;
  233.  
  234. procedure TCustomTransactionQueuer.DeleteTransaction;
  235. begin
  236.    if fQueueByFile then
  237.       DeleteTransactionFromFile
  238.    else
  239.       DeleteTransactionFromMemory;
  240. end;
  241.  
  242.  
  243. function TCustomTransactionQueuer.GetStatusMessage: String;
  244. begin // Only valid if accessed just after a Queue/Check/Cancel operation
  245.  result := TransactionStatusMessages[TransactionData.TransactionStatus];
  246. end;
  247.  
  248.  
  249. // ### TCustomFileTransactionQueuer component
  250. //
  251. // This derived abtract class implements queuing by
  252. // file without governing the format of the data
  253. // within the file. Hence, a class derived from it can
  254. // override two or three methods to implement specialised
  255. // file formats/contents/encryption as required, which is
  256. // what TICVTransactionQueuer does later on in this file.
  257.  
  258. constructor TCustomFileTransactionQueuer.Create(AOwner: TComponent);
  259. begin
  260.   inherited Create(AOwner);
  261.   fQueueByFile := True;
  262. end;
  263.  
  264. procedure TCustomFileTransactionQueuer.SetQueueDirectory(const value: string);
  265. begin
  266.   if (value<>'') and (copy(value,Length(value),1)<>'\') then
  267.      fQueueDirectory := value +'\'
  268.   else
  269.      fQueueDirectory := value;
  270. end;
  271.  
  272. function TCustomFileTransactionQueuer.LoadTransactionFromFile: Boolean;
  273. var transactionFileName: string;
  274.     fs: TFileStream;
  275.     buffer: string;
  276. begin
  277.   result := False;
  278.   transactionFilename := GetProcessedFileName;
  279.   if transactionFilename<>'' then
  280.   begin // we have a spec which supports separate result files, AND we've found one
  281.     result := LoadProcessedResult(transactionFilename);
  282.     exit;
  283.   end;
  284.  
  285.   transactionFilename := GetLoadFileName;
  286.   if not FileExists(transactionFileName) then
  287.     exit; { sorry, not there }
  288.   fs := TFileStream.create(transactionFilename,fmOpenRead or fmShareDenyNone);
  289.   try
  290.     With TransactionData do Age := FileDateToDateTime(filegetdate(fs.handle));
  291.     SetLength(buffer,fs.size);
  292.     fs.read(buffer[1],fs.size);
  293.   finally
  294.     fs.free;
  295.   end;
  296.   ReadDataFromBuffer(buffer);
  297.   result := True;
  298. end;
  299.  
  300. procedure TCustomFileTransactionQueuer.DeleteTransactionFromFile;
  301. var fn: string;
  302. begin
  303.   fn := GetLoadFilename; // if any
  304.   if fn<>'' then
  305.     DeleteFile(fn);
  306.   fn := GetProcessedFilename; // if any
  307.   if fn <>'' then
  308.     DeleteFile(fn);
  309. end;
  310.  
  311. procedure TCustomFileTransactionQueuer.SaveTransactionToFile;
  312. var fs: TFileStream;
  313.     buffer: string;
  314. begin
  315.   WriteDataToBuffer(buffer);
  316.   try
  317.     fs := TFileStream.create(GetSaveFilename,fmCreate or fmShareExclusive);
  318.   except;
  319.     with TransactionData do TransactionStatus := tsInvalid;
  320.     exit;
  321.   end;
  322.  
  323.   try
  324.    fs.write(buffer[1],Length(buffer));
  325.   finally
  326.     fs.free;
  327.   end;
  328. end;
  329.  
  330. // dummy naming scheme, likely to be overridden/enhanced in derived components
  331. function TCustomFileTransactionQueuer.GetTransactionFilename: string;
  332. begin
  333.   result := QueueDirectory+TransactionData.TransactionID;
  334. end;
  335.  
  336. // for these ones, we'll assume a simple case where they are ALL the same file,
  337. // and the contents tell the story...
  338. function TCustomFileTransactionQueuer.GetLoadFilename: string;
  339. begin
  340.   result := GetTransactionFilename;
  341. end;
  342.  
  343. function TCustomFileTransactionQueuer.GetSaveFilename: string;
  344. begin
  345.   result := GetTransactionFilename;
  346. end;
  347.  
  348. function TCustomFileTransactionQueuer.GetProcessedFilename: string;
  349. begin
  350.   result := ''; // default spec is "don't use this"
  351. end;
  352.  
  353. // ### TSimpleFileTransactionQueuer component
  354. //
  355. // This fully-implemented component, derived from
  356. // TCustomFileTransactionQueuer, uses an extremely
  357. // simple (and unencrypted) format containing only
  358. // the essential transaction information in comma-delimited form
  359.  
  360. const SimpleFileTransactionExtension = '.trn';
  361.  
  362. function TSimpleFileTransactionQueuer.GetTransactionFilename: string;
  363. begin
  364.   result := inherited GetTransactionFilename + SimpleFileTransactionExtension;
  365. end;
  366.  
  367.   // set buffer from the appropriate TransactionData fields
  368. procedure TSimpleFileTransactionQueuer.WriteDataToBuffer(var buffer: string);
  369. begin
  370.   with TransactionData do
  371.     buffer :=CardNumber+','+
  372.            ExpiryYear+','+
  373.            ExpiryMonth+','+
  374.            TransactionAmount+','+
  375.            '"'+StatusMessage+'"'; // which is based on the current status
  376. end;
  377.  
  378.   // extract the appropriate TransactionData fields
  379. procedure TSimpleFileTransactionQueuer.ReadDataFromBuffer(const buffer: string);
  380. var status: TTransactionStatus;
  381. begin
  382. (* buffer is: CardNumber+','+
  383.               ExpiryYear+','+
  384.               ExpiryMonth+','+
  385.               TransactionAmount+','+
  386.               "StatusMessage";
  387. *)
  388.   with TStringList.Create do
  389.   try
  390.     CommaText := buffer; { easy way to break it out into a nice list }
  391.     with transactionData do
  392.     begin
  393.       TransactionStatus := tsInvalid;
  394.       if count < 5 then { bad data }
  395.          exit;
  396.       for status := low(TTransactionStatus) to high(TTransactionStatus) do
  397.       begin
  398.         if CompareText(Strings[4],TransactionStatusMessages[status])=0 then
  399.         begin
  400.           CardNumber := Strings[0];
  401.           ExpiryYear := Strings[1];
  402.           ExpiryMonth := Strings[2];
  403.           TransactionAmount := Strings[3];
  404.           TransactionStatus := status; { all set }
  405.           exit;
  406.         end;
  407.       end;
  408.     end;
  409.   finally
  410.     free;
  411.   end;
  412. end;
  413.  
  414. // ### TSimpleFileTransactionProcessor component
  415. //
  416. // This component is derived from TSimpleFileTransactionQueuer
  417. // and handles the checking of the queue and loading of
  418. // entries for actual processing, together with
  419. // the status settings required.
  420.  
  421. // Only one additional function -- to retrieve the next
  422. // unprocessed transaction (if any)
  423.  
  424. function TSimpleFileTransactionProcessor.GetNextTransaction;
  425. var SearchRec: TSearchRec;
  426.     FoundOne : Integer;
  427.     dotPos: Integer;
  428.     Searchfor: string;
  429. begin
  430.    result := False; { set to True if we find a new transaction }
  431.    SearchFor :=QueueDirectory+'*'+SimpleFileTransactionExtension;
  432.    FoundOne := FindFirst(SearchFor,faAnyFile,SearchRec);
  433.    try
  434.      while FoundOne=0 do
  435.      begin
  436.        with SearchRec, TransactionData do // a match if status is still "Queued"
  437.        begin
  438.           TransactionID :=ExtractFileName(name);
  439.           dotPos :=pos('.',TransactionID);
  440.           if dotPos> 0 then // should be, but let's keep it general
  441.              TransactionID :=copy(TransactionID,1, dotPos-1);
  442.           LoadTransactionFromFile; { get file contents }
  443.           if TransactionStatus = tsQueue then // ok, ready to go
  444.           begin // all set...
  445.             result := True;
  446.             exit;
  447.           end; // otherwise ignore it, keep looking
  448.        end;
  449.        FoundOne := FindNext(SearchRec);
  450.      end;
  451.    finally
  452.      SysUtils.FindClose(SearchRec);
  453.    end;
  454. end;
  455.  
  456.  
  457.  
  458. // ### TICVerifyTransactionQueuer component
  459. //
  460. // This derived component implements file-based queuing:
  461. // a) Using ICVerify's file formats and filename management
  462. // b) Recognising ICVerify's "simple mode" status responses
  463. // -- as defined for the standalone Windows ICV application
  464. // See Appendix B of the ICVerify manual for more information
  465.  
  466. // NB: For use with multiple simultaneous merchants, this direct
  467. //     "by file" approach is not sufficient. Instead, you need to
  468. //     buy the IC Verify SDK for $750. Among other things, the SDK
  469. //     explains how to go beyond the regular one-merchant setup by
  470. //     calling their DLL. You should do this from a standalone Processor
  471. //     application which reads the queued files and calls ICV's DLL,
  472. //     including appropriate Merchant information.
  473. //     In all likelihood you'd find the SimpleFile components the
  474. //     easiest route to manage interactions between the Webapp and
  475. //     your Processor app -- this component is far more complex
  476. //     than it needs to be because of the fairly specialised
  477. //     queue/response filenames and formats which the standalone
  478. //     ICV app uses.
  479.  
  480. // Also note:
  481. //     This implementation assumes we're going to queue transactions
  482. //     which represent IMMEDIATE sales. This is fine if online delivery
  483. //     is guaranteed (e.g. for software or information). However, for
  484. //     cases where a separate shipping step is required, it would be
  485. //     better to book a sale (C4), then track the transaction so
  486. //     that it can be later marked as "shipped" (C5).
  487. //     Even in the immediate sale case a "settlement" step is required
  488. //     which can be done (for all processed-but-unsettled transactions)
  489. //     using the ICV software, or via a "ST" coded request (see Appx B).
  490.  
  491. const ICVFileStub='icver';
  492.       ICVRequestExt='.req';
  493.       ICVResponseExt='.ans';
  494.       ICVTransactionListFile='transl.lst';
  495.  
  496. constructor TICVerifyTransactionQueuer.Create(aOwner: TComponent);
  497. begin
  498.   inherited Create(AOwner);
  499.   fTransactionData.TransactionType := 'C1'; // "Immediate sale" -- see ICV manual appdx B
  500.   fTransactionList := TStringList.create;
  501. end;
  502.  
  503. destructor TICVerifyTransactionQueuer.Destroy;
  504. begin
  505.   fTransactionList.free;
  506.   inherited Destroy;
  507. end;
  508.  
  509. function TICVerifyTransactionQueuer.GetFileNameForThisTransaction: string;
  510. begin
  511.   result :=''; // failure
  512.   with fTransactionList do
  513.   begin
  514.     clear;
  515.     try
  516.       if not FileExists(QueueDirectory+ICVTransactionListFile) then
  517.         exit;  // this is just to prevent IDE break-on-exception scares
  518.       LoadFromFile(QueueDirectory+ICVTransactionListFile)
  519.     except on E:exception do exit;
  520.     end;
  521.     result := Values[TransactionData.TransactionID]; // e.g 12345=000
  522.     if result<>'' then
  523.       result:=QueueDirectory+ICVFileStub+result; // --> \path\icver000
  524.   end;
  525. end;
  526.  
  527.  
  528. // seek existing "request" filename containing our transactionID
  529. function TICVerifyTransactionQueuer.GetLoadFilename: string;
  530. begin
  531.   result := GetFileNameForThisTransaction;
  532.   if result ='' then
  533.     exit;
  534.   result := result+ICVRequestExt;
  535.   if not FileExists(result) then
  536.     result := '';
  537. end;
  538.  
  539. // seek existing "request" filename containing our transactionID
  540. function TICVerifyTransactionQueuer.GetProcessedFilename: string;
  541. begin
  542.   result := GetFileNameForThisTransaction;
  543.   if result ='' then
  544.     exit;
  545.   result := result+ICVResponseExt;
  546.   if not FileExists(result) then
  547.     result := '';
  548. end;
  549.  
  550.  
  551. // seek UNIQUE "request" filename -- we use a TransactionList
  552. // which is saved to file each time, so that we can stop/start
  553. // the Webapp without loss of this vital mapping information
  554. function TICVerifyTransactionQueuer.GetSaveFilename: string;
  555. var tcount, newcount: Integer;
  556.     clashed: Boolean;
  557. begin
  558.   with fTransactionList do
  559.   begin
  560.     clear;
  561.     try
  562.       if FileExists(QueueDirectory+ICVTransactionListFile) then
  563.         LoadFromFile(QueueDirectory+ICVTransactionListFile)
  564.     except on E:exception do ;
  565.     end;
  566.     newcount :=0;
  567.     repeat   // this logic could be made faster, but the search space is
  568.       clashed := False; // usually only one or two transactions, so there's little point
  569.       for tcount := 0 to pred(count) do
  570.       begin
  571.         result := Values[Names[tcount]]; // e.g 12345=000
  572.         if StrToInt(result)=newcount then
  573.         begin
  574.           clashed := True;
  575.           inc(newcount);
  576.           break;
  577.         end;
  578.       end; // for
  579.     until not clashed; // newcount is available when we exit
  580.     result:=format('%3.3d',[newcount]); // 0 --> '000'
  581.     Values[TransactionData.TransactionID] :=result;
  582.     SaveToFile(QueueDirectory+ICVTransactionListFile);
  583.     result := QueueDirectory+ICVFileStub+result+ICVRequestExt; // --> icver000.req
  584.   end;
  585. end;
  586.  
  587. procedure TICVerifyTransactionQueuer.DeleteTransactionFromFile;
  588. var tcount: Integer;
  589. begin
  590.   inherited DeleteTransactionFromFile;
  591.   // fTransactionList must be in memory and may have a record
  592.   // we need to eliminate, so as to free up the filename space
  593.   with fTransactionList do
  594.   for tcount:=0 to pred(count) do
  595.     if Names[tcount]=TransactionData.TransactionID then // yup
  596.     begin
  597.       delete(tcount);
  598.       SaveToFile(QueueDirectory+ICVTransactionListFile);
  599.       exit;
  600.     end;
  601. end;
  602.  
  603.  
  604. function TICVerifyTransactionQueuer.LoadProcessedResult(const filename: string): Boolean;
  605. var line: String;
  606. begin
  607.   result := False;
  608.   if not FileExists(filename) then
  609.     exit;
  610.   with TStringList.Create do
  611.   try
  612.     try
  613.       LoadFromFile(filename);
  614.     except on E:exception do exit;
  615.     end;
  616.     line := Strings[pred(count)];
  617.     result := True;
  618.     fTransactionData.Age := FileDateToDatetime(FileAge(filename));
  619.     if copy(line,1,1)='"' then //strip probable quotes
  620.       line := copy(line,2,maxLongint);
  621.     if copy(line,Length(line),1)='"' then
  622.       line := copy(line,1,pred(Length(line)));
  623.     // a "simple mode" response should start with Y or N, of the form:
  624.     // YMN1234B1234567 -- MN then approval code, B then reference
  625.     with TransactionData do // we'll ignore any data but Accepted, or error messages
  626.       if copy(line,1,1)='Y' then
  627.         TransactionStatus := tsAccept
  628.       else if pos('TIME OUT',line)>0 then
  629.         TransactionStatus := tsTimeOut
  630.       else if pos('INVALID',line)>0 then
  631.         TransactionStatus := tsInvalid
  632.       else
  633.         TransactionStatus := tsReject;
  634.   finally
  635.     free;
  636.   end
  637. end;
  638.  
  639.   // set buffer from the appropriate TransactionData fields
  640. procedure TICVerifyTransactionQueuer.WriteDataToBuffer(var buffer: string);
  641. begin
  642.   with TransactionData do
  643.     buffer :='"'+TransactionType+'",'+
  644.              '"WQR",'+ // Web Queueing Robot as our "clerk"
  645.              '"'+TransactionID+'",'+ // ID as our Comment
  646.              '"'+CardNumber+'",'+
  647.              '"'+ExpiryYear+ExpiryMonth+'",'+ // e.g. 9805
  648.              '"'+TransactionAmount+'"'
  649.  
  650. // for example:
  651. // "C1","WQR","Bill Jones","1234123412341234","9805","25.00"
  652. end;
  653.  
  654.   // extract the appropriate TransactionData fields
  655. procedure TICVerifyTransactionQueuer.ReadDataFromBuffer(const buffer: string);
  656. begin
  657.   fTransactionData.transactionStatus := tsQueue; // still queued, thanks
  658.   // we won't bother reading the data because for ICVerify,
  659.   // the contents of this file are unchanged, and hence
  660.   // the original (and, we expect, currently-set) transaction
  661.   // information should be correct
  662. end;
  663.  
  664. procedure Register;
  665. begin
  666.   RegisterComponents('Compress', [TSimpleFileTransactionQueuer,TSimpleFileTransactionProcessor,TICVerifyTransactionQueuer]);
  667. end;
  668.  
  669. end.
  670.